﻿////////////////////////////////////////////////////////////////////////////////////////////////////
// file:	C:\Users\Quan\Documents\Visual Studio 2010\Projects\source\cvWavelet\cvWavelet2D.cpp
//
// summary:	Implements the cv wavelet 2 d class
////////////////////////////////////////////////////////////////////////////////////////////////////

#include <string> 
#include <cufft.h> 
#include <cuda.h> 
#include <iostream> 
#include <fstream>
#include <iomanip>
#include <opencv2/opencv.hpp>
#include "dwt.hpp"
#include "cutils.hpp"


using namespace std;
using namespace cv;

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>	Main entry-point for this application. </summary>
///
/// <remarks>	Quan, 5/9/2012. </remarks>
///
/// <param name="argc">	Number of command-line arguments. </param>
/// <param name="argv">	Array of command-line argument strings. </param>
///
/// <returns>	Exit-code for the process - 0 for success, else an error code. </returns>
////////////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
	//cudaSafeCall(cudaSetDevice(2));
	/// <summary>	Declare the image input file. </summary>
	const char* iFile;

	/// <summary>	By default choose Lenna image in the solution directory. </summary>
	if(argc==1)
		iFile = "lenna.jpg";
	else 
		iFile = argv[1];

	/// <summary>	The image. </summary>
	Mat img;

	/// <summary>	Read image in grayscale mode. </summary>
	img = imread(iFile, IMREAD_GRAYSCALE);

	/// <summary>	If image does not exist, terminate program. </summary>
	if (img.empty())
	{
		cout << "Cannot read image" << endl;
		return -1;
	}

	/// <summary>	Display the source image. </summary>
	//imshow("Input", img); 

	/// <summary>	Retrieve the number of rows and columns. </summary>
	int nRows = img.rows;
	int nCols = img.cols;

	/// <summary>	Allocate 1d array of data. </summary>
	float2* data;
	data = (float2*)malloc(nRows*nCols*sizeof(float2));

	/// <summary>	Copy data from image to 1d array of data. </summary>
	for(int i=0; i<nRows; i++)
	{
		for(int j=0; j<nCols; j++)
		{
			data[i*nCols + j].x = (float)img.at<uchar>(i, j);
			data[i*nCols + j].y = 0;
		}
	}



	/// <summary>	Copy data from CPU to GPU </summary>

	float2 *d_data;
	cudaSafeCall(
		cudaMalloc((void**)&d_data, sizeof(float2)*nCols*nRows)
		);
	cudaSafeCall(
		cudaMemcpy2D(d_data, sizeof(float2)*nCols,  
		data, sizeof(float2)*nCols, 
		sizeof(float2)*nCols, nRows, 
		cudaMemcpyHostToDevice)
		);


	/// <summary>	Perform Forward Wavelet Transform </summary>
	float2 *d_wavelet;
	cudaSafeCall(
		cudaMalloc((void**)&d_wavelet, sizeof(float2)*nCols*nRows)
		);

	cuda::dwt(d_data, d_wavelet, nRows, nCols, cuda::DWT_FORWARD);

	float2* wavelet;
	wavelet = (float2*)malloc(nRows*nCols*sizeof(float2));
	cudaSafeCall(
		cudaMemcpy2D(wavelet, sizeof(float2)*nCols,  
		d_wavelet, sizeof(float2)*nCols, 
		sizeof(float2)*nCols, nRows, 
		cudaMemcpyDeviceToHost)
		);
	Mat waveletImg(nRows, nCols, CV_32FC1);
	for(int i=0; i<nRows; i++)
	{
		for(int j=0; j<nCols; j++)
		{
			waveletImg.at<float>(i, j) = wavelet[i*nCols + j].x;
		}
	}
	waveletImg.convertTo(waveletImg, CV_8UC1);
	//imshow("Forward Wavelet", waveletImg);
	imwrite("Forward Wavelet.tif", waveletImg);
	/// <summary>	Perform Inverse Wavelet Transform </summary>
	float2 *d_inverse;
	cudaSafeCall(
		cudaMalloc((void**)&d_inverse, sizeof(float2)*nCols*nRows)
		);

	cuda::dwt(d_wavelet, d_inverse, nRows, nCols, cuda::DWT_INVERSE);
	float2* inverse;
	inverse = (float2*)malloc(nRows*nCols*sizeof(float2));
	cudaSafeCall(
		cudaMemcpy2D(inverse, sizeof(float2)*nCols,  
		d_inverse, sizeof(float2)*nCols, 
		sizeof(float2)*nCols, nRows, 
		cudaMemcpyDeviceToHost)
		);
	Mat inverseImg(nRows, nCols, CV_32FC1);
	for(int i=0; i<nRows; i++)
	{
		for(int j=0; j<nCols; j++)
		{
			inverseImg.at<float>(i, j) = inverse[i*nCols + j].x;
		}
	}
	inverseImg.convertTo(inverseImg, CV_8UC1);
	//imshow("Inverse Wavelet", inverseImg);
	imwrite("Inverse Wavelet.tif", inverseImg);
	//  	/// <summary>	Perform Forward Wavelet Transform </summary>
	//  	cuda::dwt(d_data, d_data, nRows, nCols, cuda::DWT_FORWARD);
	// 
	//   
	//    	/// <summary>	Perform Inverse Wavelet Transform </summary>
	//  	cuda::dwt(d_data, d_data, nRows, nCols, cuda::DWT_INVERSE);
	//  
	// 
	//  	/// <summary>	Copy data from GPU to CPU </summary>
	// 	cudaMemcpy2D(data, sizeof(float2)*nCols, 
	// 		d_data, sizeof(float2)*nCols, 
	// 		sizeof(float2)*nCols, nRows, 
	// 		cudaMemcpyDeviceToHost);
	// 
	// 
	// 
	// 	/// <summary>	Copy data from 1d array of data to image. </summary>
	// 	img.convertTo(img, CV_32FC1);
	// 	for(int i=0; i<nRows; i++)
	// 	{
	// 		for(int j=0; j<nCols; j++)
	// 		{
	// 			img.at<float>(i, j) = data[i*nCols + j].x;
	// 		}
	// 	}
	// 
	// 	img.convertTo(img, CV_8UC1);
	// 
	// 	/// <summary>	Save the output image. </summary>
	// 	cv::imwrite("Output.jpg", img);
	// 
	// 	/// <summary>	Display the output image. </summary>
	// 	imshow("Output", img); 
	// 
	// 	/// <summary>	Wait infinitely for a key press. </summary>
	// 	waitKey();
	// 
	// 	/// <summary>	Memory deallocation. </summary> 
	// 	free(data);
	// 	cudaFree(d_data);
	// 	img.release();
	// 
	waitKey();
	return 0;
}